use core::{ops::{Deref, DerefMut}, ptr::null_mut};

use utils::MutCell;

use crate::{
    c_src::{
        osMutexAcquire, osMutexAttr_t, osMutexId_t, osMutexNew, osMutexPrioInherit,
        osMutexRecursive, osMutexRelease, osMutexRobust, osRtxMutex_t, osWaitForever,
    },
    osStatus_t,
};

use super::StaticMem;

const CB_SIZE: usize = size_of::<osRtxMutex_t>();

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

pub struct Mutex {
    id: MutCell<osMutexId_t>,
    cb_mem: StaticMem<CB_SIZE>,
}

impl Mutex {
    pub const fn uninit() -> Self {
        Self {
            id: MutCell::const_new(null_mut()),
            cb_mem: StaticMem::const_new(),
        }
    }

    pub fn free<T, F: Fn() -> T>(&self, f: F) -> T {
        unsafe {
            self.lock();
            let r = f();
            self.unlock();
            r
        }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        let attr = osMutexAttr_t {
            name: name.as_ptr() as _,
            attr_bits: osMutexPrioInherit | osMutexRecursive | osMutexRobust,
            cb_mem: self.cb_mem.as_ptr(),
            cb_size: CB_SIZE as u32,
        };
        let id = osMutexNew(&attr);
        if id.is_null() {
            panic!("mutex new fail");
        }
        *self.id.as_mut() = id;
    }

    pub unsafe fn lock(&self) {
        if osStatus_t::osOK != osMutexAcquire(*self.id, osWaitForever) {
            panic!("mutex lock fail");
        }
    }

    pub unsafe fn unlock(&self) {
        if osStatus_t::osOK != osMutexRelease(*self.id) {
            panic!("mutex unlock fail");
        }
    }
}


pub struct MutexValue<T> {
    mutex: Mutex,
    value: MutCell<T>,
}

pub struct MutexGuard<'a, T> {
    raw: &'a Mutex,
    value: &'a mut T,
}

impl<'a, T> Drop for MutexGuard<'a, T> {
    #[inline(always)]
    fn drop(&mut self) {
        unsafe { self.raw.unlock(); }
    }
}

impl<'a, T> Deref for MutexGuard<'a, T> {

    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.value
    }
}

impl <'a, T> DerefMut for MutexGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.value
    }
}

impl <T> MutexValue<T> {

    pub const fn uninit(value: T) -> Self {
        Self { mutex: Mutex::uninit(), value: MutCell::const_new(value) }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        self.mutex.init_once(name);
    }

    pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> {
        unsafe {
            self.mutex.lock();
            MutexGuard { raw: &self.mutex, value: self.value.as_mut() }
        }
    }
}







